package alg;
import java.io.*;
import java.util.ArrayList;

import data.Course;
import data.Student;
import data.StudentData;
import static conf.Configuration.*;
import static java.lang.System.currentTimeMillis;

/**
 * Runs the actual algorithm, dealing with the CourseProcessors and other such things.
 * @author Frans Honig, Maxime Serrano, Raphael Townshend
 */
public class AlgorithmTester
{
	/**
	 * How long did the algorithm take the last time we tried?
	 */
	private static long lastTimeTaken = 0;

	/**
	 * The course processor that contains all of the courses used for this particular algorithm.
	 */
	public static CourseProcessor courseProcessor;

	public static void main(String args[])
	{
		run();
	}

	/**
	 * Runs the program with the default parameters.
	 */	
	public static void run()
	{
		run(DEFAULT_OUTPUT_FILE_NAME);
	}

	/**
	 * Runs the algorithm, printing the result to the appropriate output file.
	 * @param outFile The name of the file to print to.
	 */
	public static void run(String outFile)
	{
		run(outFile, DEFAULT_COURSE_FILE_NAME, DEFAULT_STUDENT_FILE_NAME);
	}

	/**
	 * Runs the algorithm.
	 * @param outFile The output file.
	 * @param coursesFileName The file to read courses in from.
	 * @param studentFileName The file to read students in from.
	 */
	public static void run(String outFile, String coursesFileName, String studentFileName)
	{
		long first = currentTimeMillis();
		CourseProcessor cp = new CourseProcessor(coursesFileName, studentFileName);
		courseProcessor = cp;
		runAlgorithm(cp);
		printSolution(cp, outFile);
		lastTimeTaken = currentTimeMillis() - first;
	}

	/**
	 * Runs the algorithm on a given CourseProcessor.
	 * @param cp The processor containing all of the data to solve with.
	 */
	private static void runAlgorithm(CourseProcessor cp)
	{
		ArrayList<Student> students = StudentProcessor.generateStudentList(cp.studentsFileName);
		cp.fillClassesWithPotentialStudents(students);
		cp.enrollTeachers();
		cp.enrollForcedLatePeople();
		for (int i = 0; i < NUM_RESOLUTIONS; i++)
		{
			cp.resolveClasses();
		}
		optimize(cp, students);
		cp.enrollRemaining();
		cp.dealWithBadlyPlacedStudents();
		cp.runCleanup();
		cp.enrollRemaining();
		cp.dealWithBadlyPlacedStudents();
		for (int i = 0; i < 10; i++)
		{
			optimize(cp, students);
		}
		System.out.println("lol");
		ArrayList<Student> lastStudents = cp.getGlobalStudents();
		ArrayList<Student> nonPlaced = new ArrayList<Student>();
		for (Student s : lastStudents)
		{
			if (!(s.isPlaced()) && !(s.isOnTrip()))
			{
				//System.out.println(s);
				nonPlaced.add(s);
			}
		}
		
		NUM_CLASS_CHOICES++;
		//cp.fillClassesWithPotentialStudents(nonPlaced);
		for (int i = 0; i < 5; i++)
		{
			cp.resolveClasses();
		}
		optimize(cp, students);
		
		lastStudents = cp.getGlobalStudents();
		for (Student s : lastStudents)
		{
			if (!(s.isPlaced()) && !(s.isOnTrip()))
			{
				System.out.println(s);
			}
		}
	}

	public static void optimize(CourseProcessor cp, ArrayList<Student> students)
	{
		for (int i = 0; i < students.size(); i++)
		{
			for(int j = 0; j < students.size(); j++)
			{
				Student s1 = students.get(i);
				Student s2 = students.get(j);
				if(s1 != null && s2 != null)
				{
					Course c1 = cp.getCourse(s1.courseEnrolledIn);
					Course c2 = cp.getCourse(s2.courseEnrolledIn);
					if(c1 != null && c2 != null)
					{
						if(s1.calcHeuristicScore(c2.getID()) + s2.calcHeuristicScore(c1.getID())
								< s1.calcHeuristicScore(c1.getID()) + s2.calcHeuristicScore(c2.getID()))
						{
							if (s1.hasCourseAsChoice(c2) && s2.hasCourseAsChoice(c1))
							{
								swap(s1, s2, c1, c2);
							}
						}
					}

				}

			}
		}
	}

	public static void swap(Student s1, Student s2, Course c1, Course c2)
	{
		if(c1 != null && c2 != null && s1 != null && s2 != null)
		{	
			if(s2.data.studentName.equalsIgnoreCase("Jeffrey Kuan") || s1.data.studentName.equalsIgnoreCase("Jeffrey Kuan"))
			{
				System.out.println(s1);
				System.out.println(s2);
			}
			int position1 = s1.position;
			int position2 = s2.position;
			s1.position = position2;
			s2.position = position1;
			s2.courseEnrolledIn = c1.getID();
			s1.courseEnrolledIn = c2.getID();
			c1.studentsEnrolled[position1] = s2;
			c2.studentsEnrolled[position2] = s1;
			if(s2.data.studentName.equalsIgnoreCase("Jeffrey Kuan") || s1.data.studentName.equalsIgnoreCase("Jeffrey Kuan"))
			{
				System.out.println(s1);
				System.out.println(s2);
			}

		}
	}


	public static void triplicateSwap(Student s1, Student s2, Student s3, Course c1, Course c2, Course c3)
	{
		if(c1 != null && c2 != null && c3 != null)
		{
			for(int i = 0; i < c1.studentsEnrolled.length; i++)
			{
				if(c1.studentsEnrolled[i] != null && s1.equals(c1.studentsEnrolled[i]))
				{
					for(int j = 0; j < c2.studentsEnrolled.length; j++)
					{
						if(c2.studentsEnrolled[j] != null && s2.equals(c2.studentsEnrolled[j]))
						{		
							for(int k = 0; j < c3.studentsEnrolled.length; j++)
							{
								if(c3.studentsEnrolled[k] != null && s3.equals(c3.studentsEnrolled[k]))
								{
									c1.studentsEnrolled[i] = s2;
									c2.studentsEnrolled[j] = s3;
									c3.studentsEnrolled[k] = s1;
									s1.courseEnrolledIn = c2.getID();
									s2.courseEnrolledIn = c3.getID();
									s3.courseEnrolledIn = c1.getID();
									return;
								}
							}
						}
					}
				}
			}
		}
	}

	/**
	 * Prints the solution contained in the CourseProcessor to the given file.
	 * @param cp The CourseProcessor containing the generated solution.
	 * @param outFile The file name to write to.
	 */
	private static void printSolution(CourseProcessor cp, String outFile)
	{
		File newFile = new File(outFile);
		PrintStream fileOut = null;
		try
		{
			createFile(newFile, true);
			fileOut = new PrintStream(newFile);
			cp.printClasses(fileOut);
		}
		catch (FileNotFoundException e)
		{
			createFile(newFile, false);
			printSolution(cp, outFile);
		}
		finally
		{
			if(fileOut != null)
			{
				fileOut.close();
			}
		}
	}

	public static String getStatistics()
	{
		String var = "";
		var += "Heuristic: " + courseProcessor.calcHeuristic() + "\n";
		var += courseProcessor.calcChoices();
		var += courseProcessor.getSizeAndExceptions();
		var += courseProcessor.getNonEnrolled();
		var += lastTimeTaken + " milliseconds taken.\n";
		return var;
	}

	/**
	 * Creates a new save file.
	 * @param newFile The file to be created.
	 * @param delete Do we delete the file beforehand?
	 */
	private static void createFile(File newFile, boolean delete)
	{
		if (delete)
		{
			newFile.delete();
		}

		try
		{
			newFile.createNewFile();
		}
		catch (Exception e)
		{
			System.err.println("Error while creating the file!");
		}
	}
}